﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace AOP
{
    [AttributeUsage(AttributeTargets.Class)]
    public sealed class AOPContextAttribute : ContextAttribute, IContributeObjectSink
    {
        public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
        {
            return new AOPHandler(nextSink, obj);
        }

        public AOPContextAttribute() : base("AOPContext")
        {
        }
    }
    [AOPContext]
    public class AOPContext : ContextBoundObject
    {

    }
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class AOPMethodAttribute : Attribute
    {
    }
    public class AOPHandler : IMessageSink
    {
        /// <summary>
        /// 下一个接收器
        /// </summary>
        private readonly IMessageSink _nextSink;
        /// <summary>
        /// 对象
        /// </summary>
        private readonly MarshalByRefObject _obj;

        public AOPHandler(IMessageSink nextSink,MarshalByRefObject obj)
        {
            _nextSink = nextSink;
            _obj = obj;
        }

        public IMessageSink NextSink
        {
            get { return _nextSink; }
        }
        public MarshalByRefObject Obj
        {
            get { return _obj;}
        }
        /// <summary>
        /// 同步处理方法  
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public IMessage SyncProcessMessage(IMessage msg)
        {
            IMessage message = null;
            var callMessage = msg as IMethodCallMessage;
            if (callMessage != null)
            {
                // Before
                AOPBeforeAttribute before = null;
                List<object> list = (callMessage.MethodBase as MethodInfo).GetCustomAttributes(false).ToList();
                list.AddRange(_obj.GetType().GetCustomAttributes().ToList());
                List<object> list1 = list.Where(x => x is AOPBeforeAttribute).ToList();
                if (list1 != null && list1.Count!=0)
                {
                    before = (AOPBeforeAttribute)list1[0];
                }
                if (before != null)
                {
                    PreProceed(msg, before);
                }
                // Invoke
                message = _nextSink.SyncProcessMessage(msg);
                // After
                AOPAfterAttribute after = null;
                List<object> list2 = list.Where(x => x is AOPAfterAttribute).ToList();
                if (list2 != null && list2.Count != 0)
                {
                    after = (AOPAfterAttribute)list2[0];
                }
                if (after != null)
                {
                    PostProceed(message, after);
                }
            }
            else
            {
                message = _nextSink.SyncProcessMessage(msg);
            }
            return message;
        }

        /// <summary>
        /// 异步处理方法
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="replySink"></param>
        /// <returns></returns>
        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return null;
        }

        /// <summary>
        /// 方法执行前
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="before"></param>
        public void PreProceed(IMessage msg, AOPBeforeAttribute before)
        {
            var message = msg as IMethodMessage;
            Type type = null;
            if (StaticRes.Types.ContainsKey(before.FullClassName))
            {
                type = StaticRes.Types[before.FullClassName];
            }
            else
            {
                // 获取当前应用域中所有已加载的程序集
                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                Type type1 = Tool.TypeToExampleType(before.FullClassName, "", assemblies.ToList());
                if (type1 != null)
                {
                    StaticRes.Types.Add(before.FullClassName, type1);
                    type = type1;
                }
            }
            var param = message.Args;
            List<object> list = param.ToList();
            list.Insert(0, Obj);
            param = list.ToArray();
            type.InvokeMember(before.StaticMethodName, BindingFlags.InvokeMethod, null, null, param);
        }

        /// <summary>
        /// 方法执行后
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="after"></param>
        public void PostProceed(IMessage msg, AOPAfterAttribute after)
        {
            var message = msg as IMethodReturnMessage;
            Type type = null;
            if (StaticRes.Types.ContainsKey(after.FullClassName))
            {
                type = StaticRes.Types[after.FullClassName];
            }
            else
            {
                // 获取当前应用域中所有已加载的程序集
                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                Type type1 = Tool.TypeToExampleType(after.FullClassName, "", assemblies.ToList());
                if (type1 != null)
                {
                    StaticRes.Types.Add(after.FullClassName, type1);
                    type = type1;
                }
            }
            var param = message.ReturnValue;
            List<object> list = new List<object>() { Obj, param };
            type.InvokeMember(after.StaticMethodName, BindingFlags.InvokeMethod, null, null, list.ToArray());
        }
    }

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public class AOPBeforeAttribute : Attribute
    {
        public string FullClassName;
        public string StaticMethodName;

        public AOPBeforeAttribute(string fullClassName, string staticMethodName)
        {
            FullClassName = fullClassName;
            StaticMethodName = staticMethodName;
        }
    }
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public class AOPAfterAttribute : Attribute
    {
        public string FullClassName;
        public string StaticMethodName;

        public AOPAfterAttribute(string fullClassName, string staticMethodName)
        {
            FullClassName = fullClassName;
            StaticMethodName = staticMethodName;
        }
    }


}
